package com.leyou.search.service;

import com.leyou.item.pojo.Brand;
import com.leyou.item.pojo.Category;
import com.leyou.item.pojo.SpecParam;
import com.leyou.search.clients.BrandClient;
import com.leyou.search.clients.CategoryClient;
import com.leyou.search.clients.GoodsClient;
import com.leyou.search.clients.SpecificationClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.utils.SearchRequest;
import com.leyou.search.utils.SearchResult;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private SpecificationClient specClient;

    @Autowired
    private GoodsClient goodsClient;


    private static final Logger logger = LoggerFactory.getLogger(IndexService.class);




    public SearchResult search(SearchRequest searchRequest) {

        String key = searchRequest.getKey();
        if (StringUtils.isBlank(key)) return null;

        //准备分页参数
        Integer page = searchRequest.getPage() - 1;
        Integer size = searchRequest.getSize();

        //构建查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //通过sourceFilter设置返回的字段,只需要id skus subTitle
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id", "skus", "subTitle"}, null));


        //使用抽取的查询构造方法,构造一个查询条件对象
        QueryBuilder query = buildBasicQueryWithFilter(searchRequest);

        //对key进行全文检索查询
        queryBuilder.withQuery(query);

        //分页
        queryBuilder.withPageable(PageRequest.of(page, size));

        //排序
        String sortBy = searchRequest.getSortBy();
        Boolean descending = searchRequest.getDescending();
        if (StringUtils.isNotBlank(sortBy)) {
            queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(descending ? SortOrder.DESC : SortOrder.ASC));
        }


        //聚合
        String categoryAggName = "category";
        String brandAggName = "brand";

        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));


        //查询获得结果
        AggregatedPage<Goods> goodsPage = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());

//获取聚合结果

        List<Category> categoryList = getCategories(categoryAggName, goodsPage);

        List<Brand> brandList = getBrands(brandAggName, goodsPage);

        //规格参数过滤

        List<Map<String, Object>> specs = null;
        if (null != categoryList && categoryList.size() == 1) {
            specs = getSpecs(categoryList.get(0).getId(),query);  //抽取方法
        }


        //封装结果并返回
        //  总条数
        long total = goodsPage.getTotalElements();
        //  总页数
        long totalPages = goodsPage.getTotalPages();

        return new SearchResult(total, totalPages, goodsPage.getContent(), categoryList, brandList, specs);
    }




    private List<Brand> getBrands(String brandAggName, AggregatedPage<Goods> goodsPage) {
        List<Brand> brandList = null;
        try {
            List<Long> brandIds = getLongs(brandAggName, goodsPage);

            brandList = this.brandClient.queryBrandByBids(brandIds);

        } catch (Exception e) {
            logger.error("品牌聚合出现异常：", e);
        }
        return brandList;
    }



    private List<Category> getCategories(String categoryAggName, AggregatedPage<Goods> goodsPage) {
        List<Category> categoryList = null;
        try {
            List<Long> cids = getLongs(categoryAggName, goodsPage);

            List<String> categoryNames = this.categoryClient.queryNameByIds(cids);

            categoryList = new ArrayList<>();

            for (int i = 0; i < categoryNames.size(); i++) {
                Category category = new Category();
                category.setName(categoryNames.get(i));
                category.setId(cids.get(i));
                categoryList.add(category);
            }
        } catch (Exception e) {
            logger.error("品牌聚合出现异常：", e);
        }
        return categoryList;
    }

    private List<Long> getLongs(String aggName, AggregatedPage<Goods> goodsPage) {
        LongTerms categoryAggs = (LongTerms) goodsPage.getAggregation(aggName);
        //获取桶,根据桶中的key获取分类名称
        List<LongTerms.Bucket> buckets = categoryAggs.getBuckets();

        List<Long> cids = new ArrayList<>();
        buckets.forEach(bucket -> cids.add(bucket.getKeyAsNumber().longValue()));
        return cids;
    }


    private List<Map<String, Object>> getSpecs(Long cid, QueryBuilder query) {

        List<Map<String, Object>> specs = new ArrayList<>();

        List<SpecParam> params = this.specClient.querySpecParam(null, cid, null, true);

        NativeSearchQueryBuilder querySpecBuilder = new NativeSearchQueryBuilder();

        querySpecBuilder.withQuery(query);

        params.forEach(spec -> {
            String aggsKey = spec.getName();

            querySpecBuilder.addAggregation(AggregationBuilders.terms(aggsKey).field("specs." + aggsKey + ".keyword"));
        });

        AggregatedPage<Goods> search = (AggregatedPage<Goods>) this.goodsRepository.search(querySpecBuilder.build());


        params.forEach(param -> {

            String name = param.getName();

            StringTerms terms = (StringTerms) search.getAggregation(name);

            List<StringTerms.Bucket> buckets = terms.getBuckets();

            List<String> bucks = new ArrayList<>();
            buckets.forEach(bucket -> {
                bucks.add(bucket.getKeyAsString());
            });

            Map<String, Object> specMap = new HashMap<>();
            specMap.put("k", name);
            specMap.put("options", bucks);
            specs.add(specMap);
        });

        return specs;
    }


    //构建基本查询条件
    private QueryBuilder buildBasicQueryWithFilter(SearchRequest request) {

        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

        //基本查询条件
        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));

        //过滤条件构建器
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();

        //整理条件构建器
        Map<String, String> filter = request.getFilter();
        for (Map.Entry<String, String> entry : filter.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();

            //商品分类和品牌已聚合
            if (key != "cid3" && key != "brandId") {
                key = "specs." + key + ".keyword";
            }

            filterQueryBuilder.must(QueryBuilders.termQuery(key, value));
        }

        queryBuilder.filter(filterQueryBuilder);
        return queryBuilder;
    }



}
